package com.gitee.wsl.mathematics.vector.vec4

import com.gitee.wsl.mathematics.vector.Vector
import com.gitee.wsl.mathematics.vector.VectorD
import com.gitee.wsl.mathematics.vector.VectorF
import com.gitee.wsl.mathematics.vector.vec2.Vector2
import com.gitee.wsl.mathematics.vector.vec3.Vector3

typealias Vec4F = Vector4<Float,*>

typealias Vec4D = Vector4<Double,*>


interface Vector4<T:Number,V: Vector4<T, V>>: Vector<T, V> {

    val vec2  get() = createVec2(x,y)
    val vec3  get() = createVec3(x,y,z)
    val vec4  get() = this

    val xy   get() = createVec2(x,y)
    val xz   get() = createVec2(x,z)
    val xw   get() = createVec2(x,w)
    val yz   get() = createVec2(y,z)
    val yw   get() = createVec2(y,w)
    val zw   get() = createVec2(z,w)
    val xyz  get() = createVec3(x,y,z)
    val xyw  get() = createVec3(x,y,w)
    val xzw  get() = createVec3(x,z,w)
    val yzw  get() = createVec3(y,z,w)

    val x:T
        get() = proxy[0]

    val y:T
        get() = proxy[1]

    val z
        get() = proxy[2]

    val w
        get() = proxy[3]

    /** Calculates [Vector3] by dividing [x], [y], [z] by [w]. */
    val div get() = createVec3(x / w, y / w, z / w)

    fun createVec2(a:T,b:T):Vector2<T, *>

    fun createVec3(a:T,b:T,c:T):Vector3<T, *>

    override val squaredLength get() = x * x + y * y + z * z + w * w

    override fun create(values: List<T>):V = create(values[0],values[1],values[2],values[3])

    fun create(x:T,y:T,z:T,w:T):V

    /*
    override operator fun div(other: V) = create(x / other.x, y / other.y, z / other.z, w / other.w)
    override operator fun times(other: V) = create(x * other.x, y * other.y, z * other.z, w * other.w)
    override operator fun rem(other: V) = create(x % other.x, y % other.y, z % other.z, w % other.w)

    override val abs get() = create(abs(x), abs(y), abs(z), abs(w))
    override val sign get() = create(sign(x), sign(y), sign(z), sign(w))
    override val ceil get() = create(ceil(x), ceil(y), ceil(z), ceil(w))
    override val floor get() = create(floor(x), floor(y), floor(z), floor(w))
    override val round get() = create(round(x), round(y), round(z), round(w))

    override operator fun unaryMinus(): V = create(-x, -y, -z, -w)

    override operator fun plus(other: V) = create(x + other.x, y + other.y, z + other.z, w + other.w)

    override operator fun minus(other: V) = create(x - other.x, y - other.y, z - other.z, w - other.w)

    override operator fun times(a: Number) = create(x * a.toDouble(), y * a.toDouble(), z * a.toDouble(), w * a.toDouble())

    override operator fun div(a: Number) = create(x / a.toDouble(), y / a.toDouble(), z / a.toDouble(), w / a.toDouble())

    override fun divBy(a: Number) = create( x.divBy(a),  y.divBy(a), z.divBy(a), w.divBy(a))

    override operator fun rem(a: Number) = create(x % a.toDouble(), y % a.toDouble(), z % a.toDouble(), w % a.toDouble())
    override fun remBy(a: Number) = create( x.remBy(a),  y.remBy(a),  z.remBy(a),  w.remBy(a))
*/
    /**The distance from this vector to [other].*/
    //override infix fun distanceTo(other: V) = (other - this).length

    /**The normal direction from this vector to [other].*/
    //override infix fun directionTo(other: V) = (other - this).normalized

    //override infix fun dot(other: V) = x * other.x + y * other.y + z * other.z + w * other.w

    /**The projection of this vector onto [other].*/
    //override infix fun projectOn(other: V) = other * dot(other) / other.sqrLength

    operator fun component1() = x
    operator fun component2() = y
    operator fun component3() = z
    operator fun component4() = w

}

interface Vector4F<V: Vector4<Float, V>>: Vector4<Float, V>, VectorF<V> {

}

interface Vector4D<V: Vector4<Double, V>>: Vector4<Double, V>, VectorD<V> {

}